package com.hui.stock.security.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.hui.stock.constant.StockConstant;
import com.hui.stock.security.user.LoginUserDetail;
import com.hui.stock.utils.JwtTokenUtil;
import com.hui.stock.vo.req.LoginReqVo;
import com.hui.stock.vo.resp.LoginRespVo;
import com.hui.stock.vo.resp.R;
import com.hui.stock.vo.resp.ResponseCode;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;

/**
 * @author: ymh
 * @date: 2025/1/12 14:38
 * @description: 认证过滤器 作用：认证用户信息并颁发票据
 */
public class JwtLoginAuthenticationFilter extends AbstractAuthenticationProcessingFilter {

    private RedisTemplate<String,String> redisTemplate;

    /**
     * 自定义构造器，传入登录认证的url地址
     * @param loginUrl
     */
    public JwtLoginAuthenticationFilter(String loginUrl) {
        super(loginUrl);
    }

    public void setRedisTemplate(RedisTemplate<String, String> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    /**
     * POST
     * data:{username:" ",password:" ",....}
     * 尝试去 认证 的方法
     * @param request
     * @param response
     * @return
     * @throws AuthenticationException
     * @throws IOException
     * @throws ServletException
     */
    @Override
    public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException, IOException, ServletException {
        //请求必须是post 且请求格式是application/json格式
        if (!request.getMethod().equalsIgnoreCase("POST")
                ||!(request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE)
                || request.getContentType().equalsIgnoreCase(MediaType.APPLICATION_JSON_UTF8_VALUE))) {
            throw new AuthenticationServiceException("Authentication method not supported: " + request.getMethod());
        } else {
            //TODO 获取post请求ajax传入的数据流  反序列化
            LoginReqVo reqVo = new ObjectMapper().readValue(request.getInputStream(), LoginReqVo.class);
            //检查验证码是否输入正确
            // 根据sessionID获取redis中的验证码
            String codeKey = StockConstant.CHECK_PREFIX + reqVo.getSessionId();
            String storedCode = redisTemplate.opsForValue().get(codeKey);
            //设置响应格式和编码
            response.setContentType(MediaType.APPLICATION_JSON_VALUE);
            response.setCharacterEncoding("UTF-8");
            if (StringUtils.isBlank(storedCode)||!storedCode.equals(reqVo.getCode())){
                R<Object> error = R.error(ResponseCode.DATA_ERROR);
                String jsonData = new ObjectMapper().writeValueAsString(error);
                //给前端响应错误信息
                response.getWriter().write(jsonData);
                return null;
            }

            String username = reqVo.getUsername();
            username = username != null ? username : "";
            username = username.trim();
            String password = reqVo.getPassword();
            password = password != null ? password : "";
            //组装需要认证的票据对象
            UsernamePasswordAuthenticationToken authRequest = new UsernamePasswordAuthenticationToken(username, password);
            //交给认证管理器去认证票据对象
            return this.getAuthenticationManager().authenticate(authRequest);
        }
    }

    /**
     * 认证成功后
     * @param request
     * @param response
     * @param chain 过滤器链
     * @param authResult 认证对象信息
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult) throws IOException, ServletException {
        LoginUserDetail principal = (LoginUserDetail) authResult.getPrincipal();
        String username = principal.getUsername();
        Collection<GrantedAuthority> authorities = principal.getAuthorities();
        //生成票据信息
        String token = JwtTokenUtil.createToken(username, authorities.toString());
        //构建响应实体对象
        LoginRespVo loginRespVo = new LoginRespVo();
        BeanUtils.copyProperties(principal,loginRespVo);
        loginRespVo.setAccessToken(token);
        //设置响应格式
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //内容编码格式
        response.setCharacterEncoding("utf-8");
        response.getWriter().write(new ObjectMapper().writeValueAsString(R.ok(loginRespVo)));

    }

    /**
     * 认证失败后
     * @param request
     * @param response
     * @param failed
     * @throws IOException
     * @throws ServletException
     */
    @Override
    protected void unsuccessfulAuthentication(HttpServletRequest request, HttpServletResponse response, AuthenticationException failed) throws IOException, ServletException {
        //响应数据格式
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        //编码格式
        response.setCharacterEncoding("UTF-8");
        //响应
        response.getWriter().write(new ObjectMapper().writeValueAsString(R.error(ResponseCode.ERROR)));
    }
}
